/*
 * Copyright (C) 2022 Positec.
 *
 *
 * Author:
 *         Xi.Huang@positecgroup.com
 *
 *    1. Init version 03/20/23.
 */

#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <linux/rtc.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <stddef.h>
#include <pthread.h>
#include <stdint.h>
#include "timesync_client_uapi.h"

/* pps device name.*/
#define TIMESYNC_CLIENT_DEV_NAME "/dev/pps_client"

/* The string length of IP address, consider the ipv6 extend.*/
#define IP_ADDRESS_STRING_LENGTH (64)

enum _PPS_IOCTL_CMD
{
    CUSTOM_SYSTEM_TIME_SETTING_CMD = 0x5A,
    GPS_SYNC_TIME_SETTING_CMD,
    PAUSE_PPS_CMD,
    RESUME_PPS_CMD,
    PRINT_PPS_TIMEOUT_TIME_PPS_CMD,
    PRINT_TIME_PPS_CMD,
    REQUEST_TIME_PPS_CMD,
    UDP_INIT_PPS_CMD,
    PPS_CLIENT_TIME_SYNC_RESULT_GET_CMD,
    TIME_SYNC_SIGNAL_OUTPUT_ENABLE_CMD
};

/*
 *
 *  IP informations
 *
 */
struct _ip_infos_str
{
    char server_ip_addr[IP_ADDRESS_STRING_LENGTH + 1];
    char client_ip_addr[IP_ADDRESS_STRING_LENGTH + 1];
    uint16_t server_port;
    uint16_t client_port;
};

struct _pps_time
{
    unsigned long sec;
    unsigned long nsec;
} __attribute__((packed));

struct _time_sync_signal_output_enable_str
{
    int enable;            /* 1, enable; 0, disable.*/
    int max_output_number; /* max pulse output totals.*/
};

/*
 * brief:       Check the str is or not a digital string.
 * parameters:  @str: Input string.
 * @return   :  0, is not a digital string; 1, is a digital string.
 *
 */
int isdigitstr(char *str)
{
    return (strspn(str, "0123456789") == strlen(str));
}

/*
 * brief:
 *       Init the pps client, init network attributes.
 *
 * @server_ip:
 *             IP address of pps server.
 * @client_ip: IP address of pps client.
 *
 * @server_port:
 *             server process port.
 * @client_port:
 *             client process port.
 *
 * @return:
 *         0, init successful; nozero, init failed.
 *
 */
int timesync_client_init(const char *server_ip, const char *client_ip, const uint16_t server_port, const uint16_t client_port)
{
    /* File identity of pps client device.*/
    int pps_client_fd = -1;
    struct _ip_infos_str ip_infos_str = {0};

    /* Open the timesync client device.*/
    pps_client_fd = open(TIMESYNC_CLIENT_DEV_NAME, O_RDWR);

    if (pps_client_fd < 0)
    {
        printf("%s open failed.\n", TIMESYNC_CLIENT_DEV_NAME);
        return -1;
    }

    ip_infos_str.server_port = server_port;
    ip_infos_str.client_port = client_port;
    memcpy(ip_infos_str.server_ip_addr, server_ip, strlen(server_ip));
    memcpy(ip_infos_str.client_ip_addr, client_ip, strlen(client_ip));

    if (ioctl(pps_client_fd, UDP_INIT_PPS_CMD, &ip_infos_str))
    {
        printf("!!![timesync]pps client udp init failed!\n");
        close(pps_client_fd);
        return -2;
    }

    close(pps_client_fd);
    return 0;
}

/*
 * brief:
 *          Get the ACK of time sync.
 * @parameters:
 *          None.
 * @return:
 *          0, time sync setting successful of client; nozero, time sync failed.
 *
 */
int timesync_client_time_sync_ack_get(void)
{
    int pps_client_fd = -1;
    int ret = -1;

    /* Open the timesync device.*/
    pps_client_fd = open(TIMESYNC_CLIENT_DEV_NAME, O_RDWR);

    if (pps_client_fd < 0)
    {
        printf("!!![timesync]%s open failed.\n", TIMESYNC_CLIENT_DEV_NAME);
        return -1;
    }

    ret = ioctl(pps_client_fd, PPS_CLIENT_TIME_SYNC_RESULT_GET_CMD, 0);
    if (ret)
    {
        // printf("!!![timesync] client time sync failed!\n");
        close(pps_client_fd);
        return -2;
    }

    close(pps_client_fd);
    return 0;
}

/*
 * brief:
 *          Enable time sync pulse signal to sensor.
 * @enable:
 *          A enablie bit. nozero, enable pulse output; 0, disable pulse output.
 * @pulse_number:
 *          Numbers of pulse output.
 * @return:
 *          0, successful, nozero failed.
 *
 */
int timesync_sync_pulse_output_enable(const int enable, const int pulse_number)
{
    int pps_client_fd = -1;
    int ret = -1;
    struct _time_sync_signal_output_enable_str sync_pulse_str;

    sync_pulse_str.enable = enable;
    sync_pulse_str.max_output_number = pulse_number;

    /* Open the timesync device.*/
    pps_client_fd = open(TIMESYNC_CLIENT_DEV_NAME, O_RDWR);

    if (pps_client_fd < 0)
    {
        printf("!!![timesync]%s open failed.\n", TIMESYNC_CLIENT_DEV_NAME);
        return -1;
    }

    ret = ioctl(pps_client_fd, TIME_SYNC_SIGNAL_OUTPUT_ENABLE_CMD, &sync_pulse_str);
    if (ret)
    {
        printf("!!![timesync] sync pulse output enable failed!\n");
        close(pps_client_fd);
        return -2;
    }

    close(pps_client_fd);
    return 0;
}
